/**
 *   _                                                          _   
 *  | | ____ _ _ __ _ __ ___   __ _  ___           _ __ ___ ___| |_ 
 *  | |/ / _` | '__| '_ ` _ \ / _` |/ __|  _____  | '__/ _ | __| __|
 *  |   < (_| | |  | | | | | | (_| | (__  |_____| | | |  __|__ \ |_ 
 *  |_|\_\__,_|_|  |_| |_| |_|\__,_|\___|         |_|  \___|___/\__|
 *         
 *  Copyright  2011 Kondra, Inc. 
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.kondra.karmac.rest
{
   import flash.net.URLVariables;
   import flash.utils.getDefinitionByName;
   
   import mx.core.ClassFactory;

   /**
    * The <code>AbstractServiceEndpoint</code> is an abstract implementation 
    * of <code>IServiceEndpoint</code> and should be subclassed when creating
    * service endpoint classes.
    * 
    * @author Jared Kaplan
    */  
   public class AbstractServiceEndpoint implements IServiceEndpoint
   {
      private var m_name:String;
      private var m_resultFormat:String;
	  private var m_data:Object;
      private var m_dataFactory:IServiceDataFactory;
      
      public function AbstractServiceEndpoint() {
      }
      
	  /**
	   * Returns an <code>URLVariables</code> object for the specified
	   * data to be sent by the service request.
	   * 
	   * @param data             Values that will be sent to the service with the request
	   */  
	  protected function createUrlVariables( data:Object ) : URLVariables
	  {
		  // if the data object is already an <code>URLVariables</code> object then
		  // just return the data object
		  if( data is URLVariables )
			  return data as URLVariables;
			  
		  else if( data is String )
			  return new URLVariables( data as String );
			  
			  // convert the data object into an <code>URLVariables</code> by copying all
			  // of the data object's properties
		  else
		  {
			  var vars:URLVariables = new URLVariables();
			  
			  if( data != null )
			  {
				  for( var next:String in data )
					  vars[next] = data[next];
			  }
			  
			  return vars;
		  }
	  }
	  
	  /**
	   * Returns a merged copy of the request data set in the service endpoint
	   * property with the specified request data object.
	   * 
	   * @param data             Values that will be sent to the service with the request
	   */ 
	  protected function createParams( data:Object ) : Object
	  {
		  var params:Object = null;
		  
		  if( data != null )
		  {
			  var vars:URLVariables = createUrlVariables( data );
			  
			  params = new Object();
			  
			  if( m_data != null )
			  {
				  var defaultVars:URLVariables = createUrlVariables( m_data );
				  var next:String;
				  
				  // if parameter data property has been set in this object then
				  // first copy the objects default data values into the param sobject
				  for( next in defaultVars )
					  params[next] = defaultVars[next];
				  
				  // copy all of the values from the specified data object into
				  // the new params object
				  for( next in vars )
					  params[next] = vars[next];
			  }
			  else
				  params = vars;
		  }
		  else
			  params = createUrlVariables( m_data );
		  
		  return params;
	  }

	  //
      // IServiceEndpoint implementation
      //
      
      /**
       * @inheritDoc 
       */  
      public function getName() : String {
         return m_name;
      }
      
      /**
       * @inheritDoc 
       */  
      public function getServiceDataFactory() : IServiceDataFactory {
         return m_dataFactory;
      }
      
      /**
       * @inheritDoc 
       */  
      public function getResultFormat() : String {
         return m_resultFormat;
      }
      
      /**
       * @inheritDoc 
       */  
      public function createServiceTask( data:Object = null, completeHandler:Function = null, errorHandler:Function = null, progressHandler:Function = null ) : IServiceTask {
         throw new Error( "AbstractServiceEndpoint:createServiceTask() : method must be implemented by subclass!" );
      }
      
      /**
       * @inheritDoc 
       */  
      public function invoke( data:Object = null, completeHandler:Function = null, errorHandler:Function = null, progressHandler:Function = null ) : IServiceTask 
      {
         var task:IServiceTask = createServiceTask( data, completeHandler, errorHandler, progressHandler );
         
         task.start();
         
         return task;
      }
      
      //
      // getter/setter methods
      //
      public function get name() : String { return m_name; }
      public function set name( name:String ) : void { m_name = name; }
            
      public function get resultFormat() : String { return m_resultFormat; }
      public function set resultFormat( resultFormat:String ) : void { m_resultFormat = resultFormat; }
	  
	  public function get data() : Object { return m_data; }
	  public function set data( data:Object ) : void { m_data = data; }

	  public function get dataFactory() : IServiceDataFactory { return m_dataFactory; }
      
      public function set dataFactory( dataFactory:* ) : void 
      {
         var df:IServiceDataFactory = null;
         
         if( dataFactory != null )
         {
            if( dataFactory is IServiceDataFactory )
               df = dataFactory as IServiceDataFactory;
               
            else if( dataFactory is Class )
               df = new dataFactory() as IServiceDataFactory;
               
            else if( dataFactory is ClassFactory )
               df = dataFactory.newInstance() as IServiceDataFactory;
               
            else if( dataFactory is String )
            {
               try
               {
                  var dataFactoryClass:Class = getDefinitionByName( dataFactory ) as Class;
                  
                  if( dataFactoryClass != null )
                     df = new dataFactoryClass() as IServiceDataFactory;
               }
               catch( ex:Error ) {
               }
            }
         }
         
         m_dataFactory = df;
      }   
   }
}